{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e8a1ef84",
   "metadata": {},
   "source": [
    "# Overview of Domains and Subdomains\n",
    "\n",
    "Subdomains are a key feature of OpenPNM, but the can be a source of confusion. First, let's understand what subdomains are and why they are useful. The simplest scenario is a porous materials with 2 distince layers, each with their own pore size distribution. In OpenPNM you can handle this by creating two separate Geometry objects, with each assigned to one of the layers. Each Geometry object can then compute pore size distributions independently from their own different statistical distributions, and store the values corresponding the pores in its layer. A subdomain is thus a selection of pores (and/or throats) that are related to each other but distinct from the rest of the network. Geometry objects thus define subdomains by the pores and/or throats to which they are applied or assigned. \n",
    "\n",
    "One of the repercussion of supporing subdomains at the deepest possible layer in OpenPNM is that all users must deal with them, regardelss of whether they are actually using the feature.  In other words, even materials with one domain must also define Geometry that defines a single subdomain. \n",
    "\n",
    "This notebook explores how subdomains work in OpenPNM."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "7bca81d8",
   "metadata": {},
   "outputs": [],
   "source": [
    "import openpnm as op\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40d78e93",
   "metadata": {},
   "source": [
    "Create a simple 2D cubic network for easy visualization:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f63130f3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYMAAAD5CAYAAADFqlkBAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAAADrklEQVR4nO3cwakbQRBF0VtGeTgUO3T/UBSCIyiHMFoIWp5/TgSPYuBCL2Z2NwC+tx+nBwBwnhgAIAYAiAEAiQEAiQEAiQEAiQEAiQEAiQEAiQEAiQEAiQEA1eP0gLuZmT/Vr9M7Ptyz+nl6xH/Ana597e7v0yPuYPzC+r1mZnd3Tu/4ZG70Gne65kbv45kIADEAQAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwASAwAqB6nB9zQc2b29IgP99eNXuJO156nB9zF7PrW3mlmdnfn9I5P5kavcadrbvQ+nokAEAMAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACAxACA6nF6wA09Z2ZPj/hwf93oJe507Xl6wF3Mrm/tnWZmd3dO7/hkbvQad7rmRu/jmQgAMQBADABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDABIDACoHqcH3NDXzOzpER/u6UYvcadrX6cH3MXs+tYAvjvPRACIAQBiAEBiAEBiAEBiAEBiAEBiAEBiAEBiAEBiAEBiAEBiAED1D36MW4U+jjmCAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "pn = op.network.Cubic([4, 4, 1])\n",
    "fig, ax = plt.subplots(1, 1)\n",
    "ax = op.topotools.plot_connections(network=pn, c='k', ax=ax)\n",
    "plt.axis(False);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5201591",
   "metadata": {},
   "source": [
    "Label the pores on the left and right to indicate that the Network has two subdomains:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8fbcd4cb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pn.set_label(label='layer1', pores=range(8))\n",
    "pn.set_label(label='layer2', pores=range(8, 16))\n",
    "op.topotools.plot_coordinates(network=pn, pores=pn.pores('layer1'), c='r', markersize=100, ax=ax)\n",
    "op.topotools.plot_coordinates(network=pn, pores=pn.pores('layer2'), c='c', markersize=100, ax=ax)\n",
    "fig  # Re-show figure"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "545ac388",
   "metadata": {},
   "source": [
    "## The Preferred Way to Work with Subdomains\n",
    "\n",
    "Create two Geometry objects, with one assigned to each set of pores (i.e. 'layer1' and 'layer2').  Let's also assign all the throats to ``geo1``. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "cba40533",
   "metadata": {},
   "outputs": [],
   "source": [
    "geo1 = op.geometry.GenericGeometry(network=pn, pores=pn.pores('layer1'), throats=pn.Ts)\n",
    "geo2 = op.geometry.GenericGeometry(network=pn, pores=pn.pores('layer2'))\n",
    "air = op.phases.Air(network=pn, name='air')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e148a388",
   "metadata": {},
   "source": [
    "The \"preferred\" way to create Physics objects is to assign them to a specific Phase AND Geometry upon instantiation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "7f40acfc",
   "metadata": {},
   "outputs": [],
   "source": [
    "phys1 = op.physics.GenericPhysics(network=pn, phase=air, geometry=geo1)\n",
    "phys2 = op.physics.GenericPhysics(network=pn, phase=air, geometry=geo2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e7bc50bb",
   "metadata": {},
   "source": [
    "The ``project`` has a useful feature for studying the associations between Geometry, Phase, and Physics objects called the ``grid``. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "cf7e8779",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+---------+\n",
      "| net_01 | air     |\n",
      "+--------+---------+\n",
      "| geo_01 | phys_01 |\n",
      "| geo_02 | phys_02 |\n",
      "+--------+---------+\n"
     ]
    }
   ],
   "source": [
    "proj = pn.project\n",
    "print(pn.project.grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2aa43821",
   "metadata": {},
   "source": [
    "When creating Physics objects as done above, all the associations between which pores and throats belong to each Physics is taken care of automatically. Essentially, all the pores/throats of ``geo1`` are assigned to ``phys1`` and simlarly all the pores/throats of ``geo2`` are assigned to ``phys2``. However, it is possible to make these assigments manually post-instantiation as will be demonstrated below, but first let's talk about how these associations are tracked by OpenPNM. When a Geometry object is created, two label arrays are added to the Network with ``True`` indicating the locations where that Geometry applies.  Let's see this by printing the Network:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "e5d7ccc8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "openpnm.network.Cubic : net_01\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "#     Properties                                    Valid Values\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "1     pore.coords                                      16 / 16   \n",
      "2     throat.conns                                     24 / 24   \n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "#     Labels                                        Assigned Locations\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "1     pore.all                                      16        \n",
      "2     pore.back                                     4         \n",
      "3     pore.front                                    4         \n",
      "4     pore.geo_01                                   8         \n",
      "5     pore.geo_02                                   8         \n",
      "6     pore.internal                                 16        \n",
      "7     pore.layer1                                   8         \n",
      "8     pore.layer2                                   8         \n",
      "9     pore.left                                     4         \n",
      "10    pore.right                                    4         \n",
      "11    pore.surface                                  12        \n",
      "12    throat.all                                    24        \n",
      "13    throat.geo_01                                 24        \n",
      "14    throat.geo_02                                 0         \n",
      "15    throat.internal                               24        \n",
      "16    throat.surface                                12        \n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n"
     ]
    }
   ],
   "source": [
    "print(pn)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9863292",
   "metadata": {},
   "source": [
    "As can be seen, there is a label array called 'pore.geo_01' that is ``True`` in 8 locations.  We can see which locations using ``np.where``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ef3d695d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 1 2 3 4 5 6 7]\n"
     ]
    }
   ],
   "source": [
    "print(np.where(pn['pore.geo_01'])[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e42dee74",
   "metadata": {},
   "source": [
    "And similarly, for ``geo2``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "9aa67059",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 8  9 10 11 12 13 14 15]\n"
     ]
    }
   ],
   "source": [
    "print(np.where(pn['pore.geo_02'])[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "557b69fb",
   "metadata": {},
   "source": [
    "These values are the locations in each array where ``True`` was found.  Note that they correspond to the pore locations used when creating ``geo1`` and ``geo2``.\n",
    "\n",
    "> NOTE: We **cannot** just change the values in these arrays to change the locations where ``geo1`` and ``geo2`` are applied.  There are several things that need to go on behind the scenes.  For instance, if we want to transfer half the throats from ``geo1`` to ``geo2``, OpenPNM must also move any numerical data on ``geo1``, which requires resizes the arrays on both objects. *Instead we must use the ``set_locations`` method.*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1922fa4c",
   "metadata": {},
   "source": [
    "## Working with Subdomains Manually\n",
    "\n",
    "Let's recreate the above multidomain network to illustrate how to manually set locations and associations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "d93d95c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "pn = op.network.Cubic([4, 4, 1])\n",
    "pn.set_label(label='layer1', pores=range(8))\n",
    "pn.set_label(label='layer2', pores=range(8, 16))\n",
    "geo1 = op.geometry.GenericGeometry(network=pn, pores=pn.pores('layer1'))\n",
    "geo2 = op.geometry.GenericGeometry(network=pn, pores=pn.pores('layer2'))\n",
    "air = op.phases.Air(network=pn, name='air')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d867281",
   "metadata": {},
   "source": [
    "Next, create two Physics objects, but don't assign them to a Phase or a Geometry object.  Note that a warning is issued that the instantiation was not able to assign the Physics to any pores or throats.  We'll add these associations manually below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "0fed7b9f",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "------------------------------------------------------------\n",
      "WARNING    : No Geometry provided, phys_01 will not be associated with any locations \n",
      "SOURCE     : openpnm.physics.GenericPhysics.__init__ \n",
      "TIME STAMP : 2021-10-24 19:22:51,263\n",
      "------------------------------------------------------------\n",
      "------------------------------------------------------------\n",
      "WARNING    : No Geometry provided, phys_02 will not be associated with any locations \n",
      "SOURCE     : openpnm.physics.GenericPhysics.__init__ \n",
      "TIME STAMP : 2021-10-24 19:22:51,266\n",
      "------------------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "phys1 = op.physics.GenericPhysics(network=pn)\n",
    "phys2 = op.physics.GenericPhysics(network=pn)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d668656",
   "metadata": {},
   "source": [
    "Physics objects have a ``phase`` attribute that points to the Phase with which it is currently associated.  Because we did not provide one when initializing ``phys1`` and ``phys2`` this will raise an exception:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "f3deb770",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cannot find a phase associated with phys_01\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    phys1.phase\n",
    "except Exception as e:\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df2bf8ba",
   "metadata": {},
   "source": [
    "We can assign a ``air`` to ``phys1`` as follows, after which we can retrieve it using the ``phase`` attribute:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "893199e8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "air\n"
     ]
    }
   ],
   "source": [
    "phys1.phase = air\n",
    "print(phys1.phase.name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1eaa4f2",
   "metadata": {},
   "source": [
    "Physics objects also have ``geometry`` attribute which acts just like ``phase``.  We have not yet associated ``phys1`` with any locations so it will raise an error:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "eb5c2baf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cannot find a geometry associated with phys_01\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    phys1.geometry\n",
    "except Exception as e:\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce6e8f63",
   "metadata": {},
   "source": [
    "We can assign a Geometry in the same manner as we did for Phase above.  This will connect ``phys1`` with the locations corresponding to ``geo1``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "7d3072a1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "geo_01\n",
      "[0 1 2 3 4 5 6 7]\n"
     ]
    }
   ],
   "source": [
    "phys1.geometry = geo1\n",
    "print(phys1.geometry.name)\n",
    "print(phys1.pores())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "fab52a98",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "phys1.Np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "7909a79d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cannot set locations for a physics object that is not associated with a phase\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    phys2.geometry = geo2\n",
    "except Exception as e:\n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "dd54e94c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+---------+---------+\n",
      "| net_01 | air     | ?       |\n",
      "+--------+---------+---------+\n",
      "| geo_01 | phys_01 | ---     |\n",
      "| geo_02 | ---     | ---     |\n",
      "| ?      | ---     | phys_02 |\n",
      "| ---    | ---     | ---     |\n",
      "+--------+---------+---------+\n"
     ]
    }
   ],
   "source": [
    "proj = pn.project\n",
    "print(proj.grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56c3926f",
   "metadata": {},
   "source": [
    "This shows that *phys_02* resides in the ? column indicating that it is not associated with a phase, and it resides in the ? row indicating it is not associated with a geometry either. Let's fix this below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "db59de35",
   "metadata": {},
   "outputs": [],
   "source": [
    "phys2.phase = air\n",
    "phys2.geometry = geo2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "c31e1697",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+---------+\n",
      "| net_01 | air     |\n",
      "+--------+---------+\n",
      "| geo_01 | phys_01 |\n",
      "| geo_02 | phys_02 |\n",
      "| ---    | ---     |\n",
      "+--------+---------+\n"
     ]
    }
   ],
   "source": [
    "print(pn.project.grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a0262be",
   "metadata": {},
   "source": [
    "Now we can see that *phys_02* is associated with *geo_02* and *air*.  We can also see there are some locations, either pores or throats, that are not yet associated with a geometry object, as indicated by *---* in the first column.  This is because we did not assign any throats to a Geometry yet.  Let's fix this below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "d281295e",
   "metadata": {},
   "outputs": [],
   "source": [
    "geo3 = op.geometry.GenericGeometry(network=pn, throats=range(12))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "9124bafb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+---------+\n",
      "| net_01 | air     |\n",
      "+--------+---------+\n",
      "| geo_01 | phys_01 |\n",
      "| geo_02 | phys_02 |\n",
      "| geo_03 | ---     |\n",
      "| ---    | ---     |\n",
      "+--------+---------+\n"
     ]
    }
   ],
   "source": [
    "print(pn.project.grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c5d9e09",
   "metadata": {},
   "source": [
    "Note that we only assigned 12 throats to ``geo3``, but there were 24 throats in the network. We can either create yet another Geometry, or we can use the ``set_locations`` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "2e3cc25a",
   "metadata": {},
   "outputs": [],
   "source": [
    "geo3.set_locations(throats=range(12, 24))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "c931afe2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+---------+\n",
      "| net_01 | air     |\n",
      "+--------+---------+\n",
      "| geo_01 | phys_01 |\n",
      "| geo_02 | phys_02 |\n",
      "| geo_03 | ---     |\n",
      "+--------+---------+\n"
     ]
    }
   ],
   "source": [
    "print(pn.project.grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc1fd305",
   "metadata": {},
   "source": [
    "Now we can see that all locations are assigned to a Geometry, indicated by the lack of any rows in the first column containing *---*.  We still need to create a Physics object for these throats.  \n",
    "\n",
    "Again, let's not assign it to the throats during instantiation and do it manually afterward. But we will include a Phase:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "06b85d72",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "------------------------------------------------------------\n",
      "WARNING    : No Geometry provided, phys_03 will not be associated with any locations \n",
      "SOURCE     : openpnm.physics.GenericPhysics.__init__ \n",
      "TIME STAMP : 2021-10-24 19:22:51,560\n",
      "------------------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "phys3 = op.physics.GenericPhysics(network=pn, phase=air)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "19af93cf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+---------+\n",
      "| net_01 | air     |\n",
      "+--------+---------+\n",
      "| geo_01 | phys_01 |\n",
      "| geo_02 | phys_02 |\n",
      "| geo_03 | ---     |\n",
      "| ?      | phys_03 |\n",
      "+--------+---------+\n"
     ]
    }
   ],
   "source": [
    "print(pn.project.grid)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3334f6af",
   "metadata": {},
   "source": [
    "Because we included a Phase, this new Physics is in the appropriate column, but is in a *?* row since it is not yet associated with a Geometry. Instead of using the ``geometry`` attribute as we did above, let's use the ``set_geometry`` method, which is actually called behind the scenes when using the ``geometry`` attribute:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "d2f553a8",
   "metadata": {},
   "outputs": [],
   "source": [
    "phys3.set_geometry(geo3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d30a0c4",
   "metadata": {},
   "source": [
    "Now we can print the project's grid and see that everything is looking good:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "007d5b9b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+--------+---------+\n",
      "| net_01 | air     |\n",
      "+--------+---------+\n",
      "| geo_01 | phys_01 |\n",
      "| geo_02 | phys_02 |\n",
      "| geo_03 | phys_03 |\n",
      "+--------+---------+\n"
     ]
    }
   ],
   "source": [
    "print(pn.project.grid)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
